package org.xurui.sxl.protocol.codec;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import lombok.extern.slf4j.Slf4j;
import org.jetlinks.core.device.DeviceOperator;
import org.jetlinks.core.device.DeviceRegistry;
import org.jetlinks.core.message.*;
import org.jetlinks.core.message.codec.*;
import org.jetlinks.core.message.function.FunctionInvokeMessage;
import org.jetlinks.core.server.session.DeviceSession;
import org.jetlinks.core.spi.ServiceContext;
import org.reactivestreams.Publisher;
import org.xurui.sxl.protocol.interceptor.MyDeviceMessageSenderInterceptor;
import org.xurui.sxl.protocol.provider.CmdCodecProvider;
import org.xurui.sxl.protocol.command.CmdCodec;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;


/**
 * 上学啦设备编解码
 */
@Slf4j
public class TcpMessageCodec implements DeviceMessageCodec  {

    private ServiceContext serviceContext;

    private MyDeviceMessageSenderInterceptor senderInterceptor;

    private DeviceRegistry deviceRegistry;

    public TcpMessageCodec(ServiceContext serviceContext, MyDeviceMessageSenderInterceptor senderInterceptor, Optional<DeviceRegistry> registry){
        super();
        this.serviceContext = serviceContext;
        this.senderInterceptor = senderInterceptor;
        this.deviceRegistry = registry.get();
    }
    // 传输协议定义
    @Override
    public Transport getSupportTransport() {
        return DefaultTransport.TCP;
    }

    // 把tcp消息解码为平台消息，多用于设备上报消息到平台
    @Override
    public Publisher<? extends Message> decode(MessageDecodeContext context) {
        log.debug("开始执行解码");
        return Flux.defer(() -> {
            // 消息上下文
            FromDeviceMessageContext ctx = ((FromDeviceMessageContext) context);
            // 从上下文中获取消息字节数组
            ByteBuf byteBuf = context.getMessage().getPayload();
            byte[] payload = ByteBufUtil.getBytes(byteBuf, 0, byteBuf.readableBytes(), false);
            // 把字节流转换为字符串，根据不同设备不同协议进行解析，
            String text=new String(payload);
            if (log.isDebugEnabled()) {
                log.debug("上报消息内容{}",text);
            }
            String[] payloadArr = text.replaceAll("\\*","").split(",");
            String cmd = payloadArr[2];
            List<DeviceMessage> messages = new ArrayList<>();
            if("V4".equals(cmd)){
                cmd = payloadArr[3];
            }
            CmdCodec cmdCodec = CmdCodecProvider.getInstance().getCmdCodecByCmd(cmd);
            if(cmdCodec!=null){
                messages.addAll(cmdCodec.parsePayload(text,ctx));
            }else{
                log.error("命令【{}】没找到解码器 消息内容为【{}】",cmd,text);
            }

           // 获取设备会话信息
           DeviceSession session = ctx.getSession();
           // 如果session中没有设备信息，则为设备首次上线
           if (session.getOperator() == null) {
               DeviceOnlineMessage onlineMessage = new DeviceOnlineMessage();
               onlineMessage.setDeviceId(messages.get(0).getDeviceId());
               onlineMessage.setTimestamp(System.currentTimeMillis());
               // 返回到平台上线消息
               messages.add(onlineMessage);
               if(deviceRegistry !=null){
                   deviceRegistry.getDevice(onlineMessage.getDeviceId())
                       .doOnNext(deviceOperator -> {
                           senderInterceptor.sendWillDeviceMsgDelay(deviceOperator);
                       }).subscribe();

               }
           }
           DeviceLogMessage logMessage = new DeviceLogMessage();
           logMessage.setLog(text);
           messages.add(logMessage);
           return Flux.fromIterable(messages);
        });
    }

    // 把平台消息编码为协议传输消息，多用于平台命令下发到设备
    @Override
    public Publisher<? extends EncodedMessage> encode(MessageEncodeContext context) {
        // 从平台消息上下文中获取消息内容
        CommonDeviceMessage message = (CommonDeviceMessage) context.getMessage();

        String cmd = null;
        //1处理命令消息
        if(message instanceof FunctionInvokeMessage){
            FunctionInvokeMessage msg = (FunctionInvokeMessage) message;
            cmd = msg.getFunctionId();
        }
        log.info("开始下发指令{}，指令内容{}",cmd,message);
        if(cmd!=null){
            CmdCodec cmdCodec =  CmdCodecProvider.getInstance().getCmdCodecByCmd(cmd);
            log.info("查找到编码器{}",cmdCodec);
            if(cmdCodec!=null){
                return Mono.defer(()->{
//                    context.getDevice().getConfig()
//                   return context.getDevice().getSelfConfig("zzs");
//                }).flatMap(item->{
//                    System.out.println(item);
                    String zzs = "HQ";//TODO 应该从设备mateteConfig里面读取制造商
                    log.info("开发编码下发消息{}",message);
                    EncodedMessage deviceMessage = cmdCodec.encodeMessage(message,zzs);
                    log.info("编码下发指令完成{}",deviceMessage);
                    return Mono.just(deviceMessage);
                });
            }else{
                log.error("命令【{}】没找到编码器 消息内容为【{}】",cmd,message.toString());
            }
        }else{
            log.error("编码命令为空 消息内容为【{}】",message.toString());
        }

//
//
//        EncodedMessage encodedMessage = EncodedMessage.simple(Unpooled.wrappedBuffer(message.toString().getBytes()));
//        // 根据消息类型的不同，构造不同的消息
//        if (message instanceof ReadPropertyMessage) {
//            ReadPropertyMessage readPropertyMessage = (ReadPropertyMessage) message;
//            // 获取需要传输的字节
//            byte[] bytes = readPropertyMessage.toString().getBytes();
//            // 构造为平台传输到设备的消息体
//            encodedMessage = EncodedMessage.simple(Unpooled.wrappedBuffer(bytes));
//        }
        return Mono.empty();
    }
}
